/*
* Copyright (c) 2006-2007 Erin Catto http:
*
* This software is provided 'as-is', without any express or implied
* warranty.  In no event will the authors be held liable for any damages
* arising from the use of this software.
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked, and must not be
* misrepresented the original software.
* 3. This notice may not be removed or altered from any source distribution.
*/
//var b2World = Class.create();
//b2World.prototype =
import b2ContactManager from './b2ContactManager';
import b2Island from './b2Island';
import b2TimeStep from './b2TimeStep';
import b2CollisionFilter from './b2CollisionFilter';
import b2BroadPhase from '../collision/b2BroadPhase';
import b2BodyDef from './b2BodyDef';
import b2Body from './b2Body';
import b2Joint from './joints/b2Joint';
import b2Contact from './contacts/b2Contact';
import b2Math from '../common/math/b2Math';
import b2WorldListener from './b2WorldListener';
export default class b2World {
    constructor(worldAABB, gravity, doSleep) {
        //--------------- Internals Below -------------------
        this.m_blockAllocator = null;
        this.m_stackAllocator = null;
        this.m_broadPhase = null;
        this.m_contactManager = null;
        this.m_bodyList = null;
        this.m_contactList = null;
        this.m_jointList = null;
        this.m_bodyCount = 0;
        this.m_contactCount = 0;
        this.m_jointCount = 0;
        // These bodies will be destroyed at the next time this.step.
        this.m_bodyDestroyList = null;
        this.m_gravity = null;
        this.m_allowSleep = true;
        this.m_groundBody = null;
        this.m_listener = null;
        this.m_filter = null;
        this.m_positionIterationCount = 0;
        this.step = null;
        // initialize instance variables for references
        this.step = new b2TimeStep();
        this.m_contactManager = new b2ContactManager();
        this.m_listener = null;
        this.m_filter = b2CollisionFilter.b2_defaultFilter;
        this.m_bodyList = null;
        this.m_contactList = null;
        this.m_jointList = null;
        this.m_bodyCount = 0;
        this.m_contactCount = 0;
        this.m_jointCount = 0;
        this.m_bodyDestroyList = null;
        this.m_allowSleep = doSleep;
        this.m_gravity = gravity;
        this.m_contactManager.m_world = this;
        this.m_broadPhase = new b2BroadPhase(worldAABB, this.m_contactManager);
        var bd = new b2BodyDef();
        this.m_groundBody = this.CreateBody(bd);
    }
    //~b2World(){
    //	this.DestroyBody(this.m_groundBody);
    //	delete this.m_broadPhase;
    //}
    // Set a callback to notify you when a joint is implicitly destroyed
    // when an attached body is destroyed.
    SetListener(listener) {
        this.m_listener = listener;
    }
    // Register a collision filter to provide specific control over collision.
    // Otherwise the default filter is used (b2CollisionFilter).
    SetFilter(filter) {
        this.m_filter = filter;
    }
    // Create and destroy rigid bodies. Destruction is deferred until the
    // the next call to this.Step. This is done so that bodies may be destroyed
    // while you iterate through the contact list.
    CreateBody(def) {
        //void* mem = this.m_blockAllocator.Allocate(sizeof(b2Body));
        var b = new b2Body(def, this);
        b.m_prev = null;
        b.m_next = this.m_bodyList;
        if (this.m_bodyList) {
            this.m_bodyList.m_prev = b;
        }
        this.m_bodyList = b;
        ++this.m_bodyCount;
        return b;
    }
    // Body destruction is deferred to make contact processing more robust.
    DestroyBody(b) {
        if (b.m_flags & b2Body.e_destroyFlag) {
            return;
        }
        // Remove from normal body list.
        if (b.m_prev) {
            b.m_prev.m_next = b.m_next;
        }
        if (b.m_next) {
            b.m_next.m_prev = b.m_prev;
        }
        if (b == this.m_bodyList) {
            this.m_bodyList = b.m_next;
        }
        b.m_flags |= b2Body.e_destroyFlag;
        //b2Settings.b2Assert(this.m_bodyCount > 0);
        --this.m_bodyCount;
        //b->~b2Body();
        //b.Destroy();
        // Add to the deferred destruction list.
        b.m_prev = null;
        b.m_next = this.m_bodyDestroyList;
        this.m_bodyDestroyList = b;
    }
    CleanBodyList() {
        this.m_contactManager.m_destroyImmediate = true;
        var b = this.m_bodyDestroyList;
        while (b) {
            //b2Settings.b2Assert((b.m_flags & b2Body.e_destroyFlag) != 0);
            // Preserve the next pointer.
            var b0 = b;
            b = b.m_next;
            // Delete the attached joints
            var jn = b0.m_jointList;
            while (jn) {
                var jn0 = jn;
                jn = jn.next;
                if (this.m_listener) {
                    this.m_listener.NotifyJointDestroyed(jn0.joint);
                }
                this.DestroyJoint(jn0.joint);
            }
            b0.Destroy();
            //this.m_blockAllocator.Free(b0, sizeof(b2Body));
        }
        // Reset the list.
        this.m_bodyDestroyList = null;
        this.m_contactManager.m_destroyImmediate = false;
    }
    CreateJoint(def) {
        var j = b2Joint.Create(def, this.m_blockAllocator);
        // Connect to the world list.
        j.m_prev = null;
        j.m_next = this.m_jointList;
        if (this.m_jointList) {
            this.m_jointList.m_prev = j;
        }
        this.m_jointList = j;
        ++this.m_jointCount;
        // Connect to the bodies
        j.m_node1.joint = j;
        j.m_node1.other = j.m_body2;
        j.m_node1.prev = null;
        j.m_node1.next = j.m_body1.m_jointList;
        if (j.m_body1.m_jointList)
            j.m_body1.m_jointList.prev = j.m_node1;
        j.m_body1.m_jointList = j.m_node1;
        j.m_node2.joint = j;
        j.m_node2.other = j.m_body1;
        j.m_node2.prev = null;
        j.m_node2.next = j.m_body2.m_jointList;
        if (j.m_body2.m_jointList)
            j.m_body2.m_jointList.prev = j.m_node2;
        j.m_body2.m_jointList = j.m_node2;
        // If the joint prevents collisions, then reset collision filtering.
        if (def.collideConnected == false) {
            // Reset the proxies on the body with the minimum number of shapes.
            var b = def.body1.m_shapeCount < def.body2.m_shapeCount ? def.body1 : def.body2;
            for (var s = b.m_shapeList; s; s = s.m_next) {
                s.ResetProxy(this.m_broadPhase);
            }
        }
        return j;
    }
    DestroyJoint(j) {
        var collideConnected = j.m_collideConnected;
        // Remove from the world.
        if (j.m_prev) {
            j.m_prev.m_next = j.m_next;
        }
        if (j.m_next) {
            j.m_next.m_prev = j.m_prev;
        }
        if (j == this.m_jointList) {
            this.m_jointList = j.m_next;
        }
        // Disconnect from island graph.
        var body1 = j.m_body1;
        var body2 = j.m_body2;
        // Wake up touching bodies.
        body1.WakeUp();
        body2.WakeUp();
        // Remove from body 1
        if (j.m_node1.prev) {
            j.m_node1.prev.next = j.m_node1.next;
        }
        if (j.m_node1.next) {
            j.m_node1.next.prev = j.m_node1.prev;
        }
        if (j.m_node1 == body1.m_jointList) {
            body1.m_jointList = j.m_node1.next;
        }
        j.m_node1.prev = null;
        j.m_node1.next = null;
        // Remove from body 2
        if (j.m_node2.prev) {
            j.m_node2.prev.next = j.m_node2.next;
        }
        if (j.m_node2.next) {
            j.m_node2.next.prev = j.m_node2.prev;
        }
        if (j.m_node2 == body2.m_jointList) {
            body2.m_jointList = j.m_node2.next;
        }
        j.m_node2.prev = null;
        j.m_node2.next = null;
        b2Joint.Destroy(j, this.m_blockAllocator);
        //b2Settings.b2Assert(this.m_jointCount > 0);
        --this.m_jointCount;
        // If the joint prevents collisions, then reset collision filtering.
        if (collideConnected == false) {
            // Reset the proxies on the body with the minimum number of shapes.
            var b = body1.m_shapeCount < body2.m_shapeCount ? body1 : body2;
            for (var s = b.m_shapeList; s; s = s.m_next) {
                s.ResetProxy(this.m_broadPhase);
            }
        }
    }
    // The world provides a single ground body with no collision shapes. You
    // can use this to simplify the creation of joints.
    GetGroundBody() {
        return this.m_groundBody;
    }
    // this.Step
    Step(dt, iterations) {
        var b;
        var other;
        this.step.dt = dt;
        this.step.iterations = iterations;
        if (dt > 0.0) {
            this.step.inv_dt = 1.0 / dt;
        }
        else {
            this.step.inv_dt = 0.0;
        }
        this.m_positionIterationCount = 0;
        // Handle deferred contact destruction.
        this.m_contactManager.CleanContactList();
        // Handle deferred body destruction.
        this.CleanBodyList();
        // Update contacts.
        this.m_contactManager.Collide();
        // Size the island for the worst case.
        var island = new b2Island(this.m_bodyCount, this.m_contactCount, this.m_jointCount, this.m_stackAllocator);
        // Clear all the island flags.
        for (b = this.m_bodyList; b != null; b = b.m_next) {
            b.m_flags &= ~b2Body.e_islandFlag;
        }
        for (var c = this.m_contactList; c != null; c = c.m_next) {
            c.m_flags &= ~b2Contact.e_islandFlag;
        }
        for (var j = this.m_jointList; j != null; j = j.m_next) {
            j.m_islandFlag = false;
        }
        // Build and simulate all awake islands.
        var stackSize = this.m_bodyCount;
        //var stack = (b2Body**)this.m_stackAllocator.Allocate(stackSize * sizeof(b2Body*));
        var stack = new Array(this.m_bodyCount);
        for (var k = 0; k < this.m_bodyCount; k++)
            stack[k] = null;
        for (var seed = this.m_bodyList; seed != null; seed = seed.m_next) {
            if (seed.m_flags & (b2Body.e_staticFlag | b2Body.e_islandFlag | b2Body.e_sleepFlag | b2Body.e_frozenFlag)) {
                continue;
            }
            // Reset island and stack.
            island.Clear();
            var stackCount = 0;
            stack[stackCount++] = seed;
            seed.m_flags |= b2Body.e_islandFlag;
            ;
            // Perform a depth first search (DFS) on the constraint graph.
            while (stackCount > 0) {
                // Grab the next body off the stack and add it to the island.
                b = stack[--stackCount];
                island.AddBody(b);
                // Make sure the body is awake.
                b.m_flags &= ~b2Body.e_sleepFlag;
                // To keep islands, we don't
                // propagate islands across static bodies.
                if (b.m_flags & b2Body.e_staticFlag) {
                    continue;
                }
                // Search all contacts connected to this body.
                for (var cn = b.m_contactList; cn != null; cn = cn.next) {
                    if (cn.contact.m_flags & b2Contact.e_islandFlag) {
                        continue;
                    }
                    island.AddContact(cn.contact);
                    cn.contact.m_flags |= b2Contact.e_islandFlag;
                    other = cn.other;
                    if (other.m_flags & b2Body.e_islandFlag) {
                        continue;
                    }
                    //b2Settings.b2Assert(stackCount < stackSize);
                    stack[stackCount++] = other;
                    other.m_flags |= b2Body.e_islandFlag;
                }
                // Search all joints connect to this body.
                for (var jn = b.m_jointList; jn != null; jn = jn.next) {
                    if (jn.joint.m_islandFlag == true) {
                        continue;
                    }
                    island.AddJoint(jn.joint);
                    jn.joint.m_islandFlag = true;
                    other = jn.other;
                    if (other.m_flags & b2Body.e_islandFlag) {
                        continue;
                    }
                    //b2Settings.b2Assert(stackCount < stackSize);
                    stack[stackCount++] = other;
                    other.m_flags |= b2Body.e_islandFlag;
                }
            }
            island.Solve(this.step, this.m_gravity);
            this.m_positionIterationCount = b2Math.b2Max(this.m_positionIterationCount, b2Island.m_positionIterationCount);
            if (this.m_allowSleep) {
                island.UpdateSleep(dt);
            }
            // Post solve cleanup.
            for (var i = 0; i < island.m_bodyCount; ++i) {
                // Allow static bodies to participate in other islands.
                b = island.m_bodies[i];
                if (b.m_flags & b2Body.e_staticFlag) {
                    b.m_flags &= ~b2Body.e_islandFlag;
                }
                // Handle newly frozen bodies.
                if (b.IsFrozen() && this.m_listener) {
                    var response = this.m_listener.NotifyBoundaryViolated(b);
                    if (response == b2WorldListener.b2_destroyBody) {
                        this.DestroyBody(b);
                        b = null;
                        island.m_bodies[i] = null;
                    }
                }
            }
        }
        this.m_broadPhase.Commit();
        //this.m_stackAllocator.Free(stack);
    }
    // this.Query the world for all shapes that potentially overlap the
    // provided AABB. You provide a shape pointer buffer of specified
    // size. The number of shapes found is returned.
    Query(aabb, shapes, maxCount) {
        //void** results = (void**)this.m_stackAllocator.Allocate(maxCount * sizeof(void*));
        var results = new Array();
        var count = this.m_broadPhase.QueryAABB(aabb, results, maxCount);
        for (var i = 0; i < count; ++i) {
            shapes[i] = results[i];
        }
        //this.m_stackAllocator.Free(results);
        return count;
    }
    // You can use these to iterate over all the bodies, joints, and contacts.
    GetBodyList() {
        return this.m_bodyList;
    }
    GetJointList() {
        return this.m_jointList;
    }
    GetContactList() {
        return this.m_contactList;
    }
}
b2World.s_enablePositionCorrection = 1;
b2World.s_enableWarmStarting = 1;
;
